The label for DOID:11123 in the latest release is “Henoch-Schoenlein purpura”. A number of synonyms exist and some of these have issues (i.e. “Henoch-Sch?nlein purpura”, “ purpura”). To determine if these should be removed or updated to remove the errors, a google search was done and other names for this disease were identified (e.g. “IgA vasculitis”) but it’s not immediately clear if these are new names or which names are in primary use.

The goal of this analysis is to determine what name is in primary use at this stage. Note that this is not a genetic disease, so OMIM cannot be relied on for identifying the primary name. Other sources, such as Orphanet, often have names that are unique to those resources but are not used by the clinical or research communities and cannot, therefore, be relied on.

data_file <- here::here("data/disease_info/IgA_vasculitis-20250213.rda")
# get_ftxt_safely() will automatically get PMC articles or books and will NOT
#   fail if on errors caused by individual download failures
safe_epmc_ftxt <- purrr::safely(europepmc::epmc_ftxt, otherwise = NA, quiet = FALSE)
safe_epmc_ftxt_bk <- purrr::safely(europepmc::epmc_ftxt_book, otherwise = NA, quiet = FALSE)

get_ftxt_safely <- function(pmcid = NA, bookid = NA) {
    if (is.na(pmcid) && is.na(bookid) ) return(NA)
    if (!is.na(pmcid)) {
        list(safe_epmc_ftxt(pmcid))
    } else {
        list(safe_epmc_ftxt_bk(bookid))
    }
    cat(".")
}


# parse_ftxt_xml() parses results from get_ftxt_safely()
parse_ftxt_xml <- function(safe_ftxt_xml, xml_accessor) {
    if (!is.null(safe_ftxt_xml$error)) {
        return(paste0("ERROR: ", safe_ftxt_xml$error$message))
    }
    out <- safe_ftxt_xml$result |>
        xml2::xml_find_all(xml_accessor) |>
        xml2::xml_text()

    if (length(out) == 0) {
        out <- paste0(
            "ERROR [NO BODY]: ",
            xml2::xml_text(safe_ftxt_xml$result)
        )
        if (length(out) == 0) {
            out <- "ERROR: No text extractable"
        }
    } else if (length(out) > 1) {
        out <- DO.utils::vctr_to_string(out, delim = "%%%%%") |>
            paste0("WARNING: Multilength output, separated by %%%%%.")
    }

    out
}

These are the terms currently in DO, or that have been identified in initial searches.

terms <- c(
    "Henoch-Schoenlein purpura", # current label
    "Henoch-Schönlein purpura",
    "Henoch-Schonlein purpura",
    "Henoch-Scholein purpura", # assumed mis-spelling
    # "Henoch-Sch?nlein purpura", # currently in DO, presumed errors
    # "Henoch-Sch@nlein purpura",
    "Allergic purpura",
    "Autoimmune purpura",
    "Purpura, autoimmune",
    # presumed acronym
    "HSP",
    # new name
    "IgA vasculitis",
    "immunoglobulin A vasculitis",
    "IgAV",
    # spelling variants
    "Henoch Schoenlein purpura",
    "Henoch Schönlein purpura",
    "Henoch Schonlein purpura",
    "Henoch Scholein purpura",
    "Henoch Schoelein purpura"
)

Begin by searching EuropePMC for articles that contain one or more exact matches to these terms using the default search. This search will exclude abbreviations since they are likely to find matches to irrelevant content (e.g. HSP = heat shock proteins). Save output to file, to avoid potential of repeat API call.

# exclude abbreviations when searching for publications (too likely to 
search_str <- paste0(
    'OPEN_ACCESS:y AND (',
    paste0('"', terms[stringr::str_length(terms) > 4] , '"', collapse = " OR "),
    ')'
)

if (!file.exists(data_file)) {
    res <- europepmc::epmc_search(search_str, synonym = FALSE, limit = 20000)

    save(res, file = data_file)
} else {
    load(data_file)
}

The number of publication hits (4,470), even limited to OPEN ACCESS, is greater than can be reasonably be processed using all the full text articles, but perhaps a sample of 1/4 would do. This sample will be done grouped by year to ensure representation over time and exclude preprints and retractions, or any publications with full-text unavailable (i.e. no PMCID or Book ID).

# if (!exists("res_ftxt")) {
    res_ftxt <- res_sample |>
        dplyr::rowwise() |>
        dplyr::mutate(ft_xml = get_ftxt_safely(pmcid, bookid))
...................
Request failed [404]. Retrying in 2 seconds...
Request failed [404]. Retrying in 2.5 seconds...
Error: Not Found (HTTP 404). Failed to retrieve full text..
..............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................

Evaluating Usage

Extracting all these values from the full text of the sample publications and all the titles (in a case-insensitive manner).

regex_str <- terms |>
    DO.utils::length_sort(decreasing = TRUE) |>
    paste0(collapse = "|")

hsp <- res_tidy |>
    dplyr::left_join(
        res_ftxt,
        by = c("id", "title", "pubYear", "pubDate", "pmcid", "bookid")
    ) |>
    dplyr::select("id", "pubDate", "title", "ft") |>
    dplyr::mutate(
        title_match = stringr::str_extract_all(
            .data$title,
            stringr::regex(regex_str, ignore_case = TRUE)
        ),
        ft_match = stringr::str_extract_all(
            .data$ft,
            stringr::regex(regex_str, ignore_case = TRUE)
        )
    ) |>
    tidyr::unnest(title_match, keep_empty = TRUE) |>
    tidyr::unnest(ft_match, keep_empty = TRUE) |>
    dplyr::mutate(ft = !is.na(ft))

The number of publications with and without matches in their titles or full text, noting whether their full-text was obtained are as follows:

hsp |>
    dplyr::summarize(
        title_match = any(!is.na(title_match)),
        ft_match = any(!is.na(ft_match)),
        ft = unique(ft),
        .by = "id"
    ) |>
    dplyr::count(ft, title_match, ft_match) |>
    dplyr::mutate(pct = round(n / sum(n) * 100, 2)) |>
    dplyr::rename(ft_obtained = "ft")

Most of the publications do not contain one of these terms, including ~20% of those that were downloaded as full text. Any non-matches will just be dropped for the analysis of names.

hsp_match <- hsp |>
    tidyr::pivot_longer(
        title_match:ft_match,
        names_to = c("source", ".value"),
        names_sep = "_",
        values_drop_na = TRUE
    ) |>
    dplyr::mutate(match = stringr::str_to_lower(.data$match)) |>
    dplyr::select(-"title") |>
    # only count once per publication and title/full-text
    DO.utils::collapse_col("match")

The terms of interest that and whether they were found is as follows:

terms_match <- tibble::tibble(
    terms = terms,
    match = stringr::str_to_lower(terms)
)

terms_match |>
    dplyr::left_join(dplyr::count(hsp_match, match), by = "match") |>
    dplyr::mutate(n = tidyr::replace_na(n, 0)) |>
    dplyr::select(-"match") |>
    dplyr::arrange(dplyr::desc(.data$n))

Only two terms weren’t found. One is likely historical and may not be available in a computable form and the other looks like it may be a misspelling.

The current name in DO is not in the top 5 most common names in the literature.

Organized by publication date and binned into year intervals, the results are as follows:

hsp_df <- hsp_match |>
    dplyr::left_join(terms_match, by = "match") |>
    dplyr::select(-"match") |>
    dplyr::rename(term = "terms") |>
    dplyr::filter(!is.na(term))

hsp_colors <- hues::iwanthue(dplyr::n_distinct(hsp_df$term))
    
ggplot2::ggplot(hsp_df) +
    ggplot2::geom_freqpoly(
        ggplot2::aes(x = pubDate, color = term),
        binwidth = 365
    ) +
    ggplot2::scale_color_manual(values = hsp_colors) +
    ggplot2::facet_wrap(~ source, ncol = 1, scales = "free_y")

Hmm… the oldest uses are quite a long time ago and make the graph a bit hard to read. Subsetting the graph to after the year 2000:

g <- ggplot2::ggplot(hsp_df) +
    ggplot2::geom_freqpoly(
        ggplot2::aes(x = pubDate, color = term, group = term),
        binwidth = 365,
        linewidth = 1
    ) +
    ggplot2::scale_color_manual(values = hsp_colors) +
    ggplot2::scale_x_date(
        date_breaks = "5 years",
        date_labels = "%Y"
    ) +
    ggplot2::facet_wrap(~ source, ncol = 1, scales = "free_y") +
    ggplot2::coord_cartesian(
        xlim = c(as.Date("2000-01-01"), as.Date("2025-01-01"))
    ) +
    ggplot2::theme_minimal()

plotly::ggplotly(g)

Based on this, it seems that for most of the DO’s history, this disease has primarily been called “Henoch-Schönlein purpura” which was the original name of the published disease. It’s likely this was not used by the DO because of potential encoding issues with the “ö” and, in the literature, it appears that using a normal “o” was next most commmon, but the current label in DO has barely been used and should likely NOT be used as the label.

Furthermore, since ~2020, the name “IgA vasculitis” has been used more frequently in publications. An NIDDK source also suggests that this disease has been renamed to IgA vasculitis: https://www.niddk.nih.gov/health-information/kidney-disease/iga-vasculitis. Although the full name “immunoglobulin A vasculitis” seems like it would be more appropriate, it is used far less frequently. Together, these suggest the primary name should be updated to “IgA vasculitis”.

The initialisms for these names are used far less frequently, with HSP being greater than IgAV. It’s possible that HSP represents something else given it’s more common (e.g. “heat shock proteins”, “hereditary spastic paraplegia”).

Confirming relevance of HSP

Just to confirm that HSP is used in conjunction with this disease name:

It seems to appear in titles. Looking at which those are:

dplyr::filter(hsp, title_match == "HSP")$title
 [1] "Online information-seeking behavior of Iranian web users on Google about Henoch-Schönlein purpura (HSP): an infodemiology study."      
 [2] "Protection of Proanthocyanidins Against HSP Serum-Induced Inflammation and Oxidative Stress on Human Umbilical Vein Endothelial Cells."
 [3] "Henoch-schönlein Purpura (HSP) in a patient on Abemaciclib."                                                                           
 [4] "A narrative review of potential drug treatments for nephritis in children with IgA vasculitis (HSP)."                                  
 [5] "A retrospective study on the characteristics of renal pathological grades in HSPN children with mild to moderate proteinuria."         
 [6] "Possible HSP reactivation post-COVID-19 vaccination and booster."                                                                      
 [7] "The Efficacy of <i>Tripterygium</i> Glycosides Combined with LMWH in Treatment of HSPN in Children."                                   
 [8] "24h Urinary Protein Levels and Urine Protein/Creatinine Ratios Could Probably Forecast the Pathological Classification of HSPN."       
 [9] "PReS-FINAL-2318: The structural and functional changes of biomembrains of FMF and HSP with children"                                   
[10] "Evaluation of antibodies against human HSP60 in patients with MPO-ANCA associated glomerulonephritis: a cohort study."                 

Well, it is used to mean this disease but clearly also used for other things. It seems like it should be added as a synonym.

CONCLUSION

New primary label: “IgA vasculitis”

Ensure all these terms are included as synonyms in the DO:

  • Henoch-Schoenlein purpura
  • Henoch-Schönlein purpura
  • Henoch-Schonlein purpura
  • Henoch-Scholein purpura
  • Allergic purpura
  • Purpura, autoimmune
  • HSP
  • IgA vasculitis
  • immunoglobulin A vasculitis
  • IgAV
  • Henoch Schoenlein purpura
  • Henoch Schönlein purpura
  • Henoch Schonlein purpura
  • Henoch Scholein purpura

Even those these terms were not found in this analysis, there’s a possibility that they have been used, historically or perhaps accidentally, so they will be left in the ontology, if they are there already but not added.

  • Autoimmune purpura
  • Henoch Schoelein purpura

On review, it appears that only “Autoimmune purpura” is already in the ontology. That will be kept and the other potential synonym will not be added.

LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgRE9JRDoxMTEyMyBuYW1lICYgc3lub255bXMiCmRhdGU6ICIyMDI1LTAyLTIxIgpvdXRwdXQ6CiAgICBodG1sX25vdGVib29rOgogICAgICAgIHRvYzogdHJ1ZQogICAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICAgIGNvZGVfZm9sZGluZzogaGlkZQotLS0KClRoZSBsYWJlbCBmb3IgRE9JRDoxMTEyMyBpbiB0aGUgbGF0ZXN0IHJlbGVhc2UgaXMgIkhlbm9jaC1TY2hvZW5sZWluIHB1cnB1cmEiLiBBIG51bWJlciBvZiBzeW5vbnltcyBleGlzdCBhbmQgc29tZSBvZiB0aGVzZSBoYXZlIGlzc3VlcyAoaS5lLiAiSGVub2NoLVNjaD9ubGVpbiBwdXJwdXJhIiwgIkhlbm9jaC1TY2hAbmxlaW4gcHVycHVyYSIpLiBUbyBkZXRlcm1pbmUgaWYgdGhlc2Ugc2hvdWxkIGJlIHJlbW92ZWQgb3IgdXBkYXRlZCB0byByZW1vdmUgdGhlIGVycm9ycywgYSBnb29nbGUgc2VhcmNoIHdhcyBkb25lIGFuZCBvdGhlciBuYW1lcyBmb3IgdGhpcyBkaXNlYXNlIHdlcmUgaWRlbnRpZmllZCAoZS5nLiAiSWdBIHZhc2N1bGl0aXMiKSBidXQgaXQncyBub3QgaW1tZWRpYXRlbHkgY2xlYXIgaWYgdGhlc2UgYXJlIG5ldyBuYW1lcyBvciB3aGljaCBuYW1lcyBhcmUgaW4gcHJpbWFyeSB1c2UuCgpUaGUgZ29hbCBvZiB0aGlzIGFuYWx5c2lzIGlzIHRvIGRldGVybWluZSB3aGF0IG5hbWUgaXMgaW4gcHJpbWFyeSB1c2UgYXQgdGhpcyBzdGFnZS4gTm90ZSB0aGF0IHRoaXMgaXMgbm90IGEgZ2VuZXRpYyBkaXNlYXNlLCBzbyBPTUlNIGNhbm5vdCBiZSByZWxpZWQgb24gZm9yIGlkZW50aWZ5aW5nIHRoZSBwcmltYXJ5IG5hbWUuIE90aGVyIHNvdXJjZXMsIHN1Y2ggYXMgT3JwaGFuZXQsIG9mdGVuIGhhdmUgbmFtZXMgdGhhdCBhcmUgdW5pcXVlIHRvIHRob3NlIHJlc291cmNlcyBidXQgYXJlIG5vdCB1c2VkIGJ5IHRoZSBjbGluaWNhbCBvciByZXNlYXJjaCBjb21tdW5pdGllcyBhbmQgY2Fubm90LCB0aGVyZWZvcmUsIGJlIHJlbGllZCBvbi4KCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShldXJvcGVwbWMpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHhtbDIpCmxpYnJhcnkoaGVyZSkKbGlicmFyeShodWVzKQpsaWJyYXJ5KHBsb3RseSkKYGBgCgpgYGB7ciBpbl9wcm9ncmVzc19kYXRhfQpkYXRhX2ZpbGUgPC0gaGVyZTo6aGVyZSgiZGF0YS9kaXNlYXNlX2luZm8vSWdBX3Zhc2N1bGl0aXMtMjAyNTAyMTMucmRhIikKYGBgCgpgYGB7ciBjdXN0b21fZnVuY3Rpb25zfQojIGdldF9mdHh0X3NhZmVseSgpIHdpbGwgYXV0b21hdGljYWxseSBnZXQgUE1DIGFydGljbGVzIG9yIGJvb2tzIGFuZCB3aWxsIE5PVAojICAgZmFpbCBpZiBvbiBlcnJvcnMgY2F1c2VkIGJ5IGluZGl2aWR1YWwgZG93bmxvYWQgZmFpbHVyZXMKc2FmZV9lcG1jX2Z0eHQgPC0gcHVycnI6OnNhZmVseShldXJvcGVwbWM6OmVwbWNfZnR4dCwgb3RoZXJ3aXNlID0gTkEsIHF1aWV0ID0gRkFMU0UpCnNhZmVfZXBtY19mdHh0X2JrIDwtIHB1cnJyOjpzYWZlbHkoZXVyb3BlcG1jOjplcG1jX2Z0eHRfYm9vaywgb3RoZXJ3aXNlID0gTkEsIHF1aWV0ID0gRkFMU0UpCgpnZXRfZnR4dF9zYWZlbHkgPC0gZnVuY3Rpb24ocG1jaWQgPSBOQSwgYm9va2lkID0gTkEpIHsKICAgIGlmIChpcy5uYShwbWNpZCkgJiYgaXMubmEoYm9va2lkKSApIHJldHVybihOQSkKICAgIGlmICghaXMubmEocG1jaWQpKSB7CiAgICAgICAgb3V0IDwtIGxpc3Qoc2FmZV9lcG1jX2Z0eHQocG1jaWQpKQogICAgfSBlbHNlIHsKICAgICAgICBvdXQgPC0gbGlzdChzYWZlX2VwbWNfZnR4dF9iayhib29raWQpKQogICAgfQogICAgY2F0KCIuIikKICAgIG91dAp9CgoKIyBwYXJzZV9mdHh0X3htbCgpIHBhcnNlcyByZXN1bHRzIGZyb20gZ2V0X2Z0eHRfc2FmZWx5KCkKcGFyc2VfZnR4dF94bWwgPC0gZnVuY3Rpb24oc2FmZV9mdHh0X3htbCwgeG1sX2FjY2Vzc29yKSB7CiAgICBpZiAoIWlzLm51bGwoc2FmZV9mdHh0X3htbCRlcnJvcikpIHsKICAgICAgICByZXR1cm4ocGFzdGUwKCJFUlJPUjogIiwgc2FmZV9mdHh0X3htbCRlcnJvciRtZXNzYWdlKSkKICAgIH0KICAgIG91dCA8LSBzYWZlX2Z0eHRfeG1sJHJlc3VsdCB8PgogICAgICAgIHhtbDI6OnhtbF9maW5kX2FsbCh4bWxfYWNjZXNzb3IpIHw+CiAgICAgICAgeG1sMjo6eG1sX3RleHQoKQoKICAgIGlmIChsZW5ndGgob3V0KSA9PSAwKSB7CiAgICAgICAgb3V0IDwtIHBhc3RlMCgKICAgICAgICAgICAgIkVSUk9SIFtOTyBCT0RZXTogIiwKICAgICAgICAgICAgeG1sMjo6eG1sX3RleHQoc2FmZV9mdHh0X3htbCRyZXN1bHQpCiAgICAgICAgKQogICAgICAgIGlmIChsZW5ndGgob3V0KSA9PSAwKSB7CiAgICAgICAgICAgIG91dCA8LSAiRVJST1I6IE5vIHRleHQgZXh0cmFjdGFibGUiCiAgICAgICAgfQogICAgfSBlbHNlIGlmIChsZW5ndGgob3V0KSA+IDEpIHsKICAgICAgICBvdXQgPC0gRE8udXRpbHM6OnZjdHJfdG9fc3RyaW5nKG91dCwgZGVsaW0gPSAiJSUlJSUiKSB8PgogICAgICAgICAgICBwYXN0ZTAoIldBUk5JTkc6IE11bHRpbGVuZ3RoIG91dHB1dCwgc2VwYXJhdGVkIGJ5ICUlJSUlLiIpCiAgICB9CgogICAgb3V0Cn0KYGBgCgoKVGhlc2UgYXJlIHRoZSB0ZXJtcyBjdXJyZW50bHkgaW4gRE8sIG9yIHRoYXQgaGF2ZSBiZWVuIGlkZW50aWZpZWQgaW4gaW5pdGlhbCBzZWFyY2hlcy4KCmBgYHtyfQp0ZXJtcyA8LSBjKAogICAgIkhlbm9jaC1TY2hvZW5sZWluIHB1cnB1cmEiLCAjIGN1cnJlbnQgbGFiZWwKICAgICJIZW5vY2gtU2Now7ZubGVpbiBwdXJwdXJhIiwKICAgICJIZW5vY2gtU2Nob25sZWluIHB1cnB1cmEiLAogICAgIkhlbm9jaC1TY2hvbGVpbiBwdXJwdXJhIiwgIyBhc3N1bWVkIG1pcy1zcGVsbGluZwogICAgIyAiSGVub2NoLVNjaD9ubGVpbiBwdXJwdXJhIiwgIyBjdXJyZW50bHkgaW4gRE8sIHByZXN1bWVkIGVycm9ycwogICAgIyAiSGVub2NoLVNjaEBubGVpbiBwdXJwdXJhIiwKICAgICJBbGxlcmdpYyBwdXJwdXJhIiwKICAgICJBdXRvaW1tdW5lIHB1cnB1cmEiLAogICAgIlB1cnB1cmEsIGF1dG9pbW11bmUiLAogICAgIyBwcmVzdW1lZCBhY3JvbnltCiAgICAiSFNQIiwKICAgICMgbmV3IG5hbWUKICAgICJJZ0EgdmFzY3VsaXRpcyIsCiAgICAiaW1tdW5vZ2xvYnVsaW4gQSB2YXNjdWxpdGlzIiwKICAgICJJZ0FWIiwKICAgICMgc3BlbGxpbmcgdmFyaWFudHMKICAgICJIZW5vY2ggU2Nob2VubGVpbiBwdXJwdXJhIiwKICAgICJIZW5vY2ggU2Now7ZubGVpbiBwdXJwdXJhIiwKICAgICJIZW5vY2ggU2Nob25sZWluIHB1cnB1cmEiLAogICAgIkhlbm9jaCBTY2hvbGVpbiBwdXJwdXJhIiwKICAgICJIZW5vY2ggU2Nob2VsZWluIHB1cnB1cmEiCikKYGBgCgoKQmVnaW4gYnkgc2VhcmNoaW5nIEV1cm9wZVBNQyBmb3IgYXJ0aWNsZXMgdGhhdCBjb250YWluIG9uZSBvciBtb3JlIGV4YWN0IG1hdGNoZXMgdG8gdGhlc2UgdGVybXMgdXNpbmcgdGhlIGRlZmF1bHQgc2VhcmNoLiBUaGlzIHNlYXJjaCB3aWxsIGV4Y2x1ZGUgYWJicmV2aWF0aW9ucyBzaW5jZSB0aGV5IGFyZSBsaWtlbHkgdG8gZmluZCBtYXRjaGVzIHRvIGlycmVsZXZhbnQgY29udGVudCAoZS5nLiBIU1AgPSBoZWF0IHNob2NrIHByb3RlaW5zKS4gX1NhdmUgb3V0cHV0IHRvIGZpbGUsIHRvIGF2b2lkIHBvdGVudGlhbCBvZiByZXBlYXQgQVBJIGNhbGwuXwoKYGBge3J9CiMgZXhjbHVkZSBhYmJyZXZpYXRpb25zIHdoZW4gc2VhcmNoaW5nIGZvciBwdWJsaWNhdGlvbnMgKHRvbyBsaWtlbHkgdG8gCnNlYXJjaF9zdHIgPC0gcGFzdGUwKAogICAgJ09QRU5fQUNDRVNTOnkgQU5EICgnLAogICAgcGFzdGUwKCciJywgdGVybXNbc3RyaW5ncjo6c3RyX2xlbmd0aCh0ZXJtcykgPiA0XSAsICciJywgY29sbGFwc2UgPSAiIE9SICIpLAogICAgJyknCikKCmlmICghZmlsZS5leGlzdHMoZGF0YV9maWxlKSkgewogICAgcmVzIDwtIGV1cm9wZXBtYzo6ZXBtY19zZWFyY2goc2VhcmNoX3N0ciwgc3lub255bSA9IEZBTFNFLCBsaW1pdCA9IDIwMDAwKQoKICAgIHNhdmUocmVzLCBmaWxlID0gZGF0YV9maWxlKQp9IGVsc2UgewogICAgbG9hZChkYXRhX2ZpbGUpCn0KYGBgCgpUaGUgbnVtYmVyIG9mIHB1YmxpY2F0aW9uIGhpdHMgKGByIGZvcm1hdChucm93KHJlcyksIGJpZy5tYXJrID0gIiwiKWApLCBldmVuIGxpbWl0ZWQgdG8gT1BFTiBBQ0NFU1MsIGlzIGdyZWF0ZXIgdGhhbiBjYW4gYmUgcmVhc29uYWJseSBiZSBwcm9jZXNzZWQgdXNpbmcgYWxsIHRoZSBmdWxsIHRleHQgYXJ0aWNsZXMsIGJ1dCBwZXJoYXBzIGEgc2FtcGxlIG9mIDEvNCB3b3VsZCBkby4gVGhpcyBzYW1wbGUgd2lsbCBiZSBkb25lIGdyb3VwZWQgYnkgeWVhciB0byBlbnN1cmUgcmVwcmVzZW50YXRpb24gb3ZlciB0aW1lIGFuZCBleGNsdWRlIHByZXByaW50cyBhbmQgcmV0cmFjdGlvbnMsIG9yIGFueSBwdWJsaWNhdGlvbnMgd2l0aCBmdWxsLXRleHQgdW5hdmFpbGFibGUgKGkuZS4gbm8gUE1DSUQgb3IgQm9vayBJRCkuCgpgYGB7cn0KcmVzX3RpZHkgPC0gcmVzIHw+CiAgICBkcGx5cjo6ZmlsdGVyKAogICAgICAgICFzdHJpbmdyOjpzdHJfZGV0ZWN0KHB1YlR5cGUsICJyZXRyYWN0fHByZXByaW50IiksCiAgICAgICAgIShpcy5uYShwbWNpZCkgJiBpcy5uYShib29raWQpKQogICAgKSB8PgogICAgZHBseXI6OnNlbGVjdCgKICAgICAgICAiaWQiLCAidGl0bGUiLCAicHViWWVhciIsIHB1YkRhdGUgPSAiZmlyc3RQdWJsaWNhdGlvbkRhdGUiLAogICAgICAgICJwbWNpZCIsICJib29raWQiKSB8PgogICAgZHBseXI6Om11dGF0ZSgKICAgICAgICBwdWJEYXRlID0gbHVicmlkYXRlOjphc19kYXRlKHB1YkRhdGUpLAogICAgICAgIHB1YlllYXIgPSBsdWJyaWRhdGU6OnllYXIocHViRGF0ZSkKICAgICkKCmlmICghZXhpc3RzKCJyZXNfc2FtcGxlIikpIHsKICAgIHJlc19zYW1wbGUgPC0gcmVzX3RpZHkgfD4KICAgICAgICBkcGx5cjo6c2xpY2Vfc2FtcGxlKHByb3AgPSAwLjI1LCBieSA9IHB1YlllYXIpCgogICAgc2F2ZShyZXMsIHJlc19zYW1wbGUsIGZpbGUgPSBkYXRhX2ZpbGUpCn0KCmlmICghZXhpc3RzKCJyZXNfZnR4dCIpKSB7CiAgICByZXNfZnR4dCA8LSByZXNfc2FtcGxlIHw+CiAgICAgICAgZHBseXI6OnJvd3dpc2UoKSB8PgogICAgICAgIGRwbHlyOjptdXRhdGUoZnRfeG1sID0gZ2V0X2Z0eHRfc2FmZWx5KHBtY2lkLCBib29raWQpKSB8PgogICAgICAgIGRwbHlyOjptdXRhdGUoZnQgPSBwYXJzZV9mdHh0X3htbChmdF94bWwsICIvL2JvZHkiKSkKICAgIAogICAgc2F2ZShyZXMsIHJlc19zYW1wbGUsIHJlc19mdHh0LCBmaWxlID0gZGF0YV9maWxlKQp9CmBgYAoKCiMgRXZhbHVhdGluZyBVc2FnZQoKRXh0cmFjdGluZyBhbGwgdGhlc2UgdmFsdWVzIGZyb20gdGhlIGZ1bGwgdGV4dCBvZiB0aGUgc2FtcGxlIHB1YmxpY2F0aW9ucyBhbmQgYWxsIHRoZSB0aXRsZXMgKGluIGEgY2FzZS1pbnNlbnNpdGl2ZSBtYW5uZXIpLgpgYGB7cn0KcmVnZXhfc3RyIDwtIHRlcm1zIHw+CiAgICBETy51dGlsczo6bGVuZ3RoX3NvcnQoZGVjcmVhc2luZyA9IFRSVUUpIHw+CiAgICBwYXN0ZTAoY29sbGFwc2UgPSAifCIpCgpoc3AgPC0gcmVzX3RpZHkgfD4KICAgIGRwbHlyOjpsZWZ0X2pvaW4oCiAgICAgICAgcmVzX2Z0eHQsCiAgICAgICAgYnkgPSBjKCJpZCIsICJ0aXRsZSIsICJwdWJZZWFyIiwgInB1YkRhdGUiLCAicG1jaWQiLCAiYm9va2lkIikKICAgICkgfD4KICAgIGRwbHlyOjpzZWxlY3QoImlkIiwgInB1YkRhdGUiLCAidGl0bGUiLCAiZnQiKSB8PgogICAgZHBseXI6Om11dGF0ZSgKICAgICAgICB0aXRsZV9tYXRjaCA9IHN0cmluZ3I6OnN0cl9leHRyYWN0X2FsbCgKICAgICAgICAgICAgLmRhdGEkdGl0bGUsCiAgICAgICAgICAgIHN0cmluZ3I6OnJlZ2V4KHJlZ2V4X3N0ciwgaWdub3JlX2Nhc2UgPSBUUlVFKQogICAgICAgICksCiAgICAgICAgZnRfbWF0Y2ggPSBzdHJpbmdyOjpzdHJfZXh0cmFjdF9hbGwoCiAgICAgICAgICAgIC5kYXRhJGZ0LAogICAgICAgICAgICBzdHJpbmdyOjpyZWdleChyZWdleF9zdHIsIGlnbm9yZV9jYXNlID0gVFJVRSkKICAgICAgICApCiAgICApIHw+CiAgICB0aWR5cjo6dW5uZXN0KHRpdGxlX21hdGNoLCBrZWVwX2VtcHR5ID0gVFJVRSkgfD4KICAgIHRpZHlyOjp1bm5lc3QoZnRfbWF0Y2gsIGtlZXBfZW1wdHkgPSBUUlVFKSB8PgogICAgZHBseXI6Om11dGF0ZShmdCA9ICFpcy5uYShmdCkpCmBgYAoKVGhlIG51bWJlciBvZiBwdWJsaWNhdGlvbnMgd2l0aCBhbmQgd2l0aG91dCBtYXRjaGVzIGluIHRoZWlyIHRpdGxlcyBvciBmdWxsIHRleHQsIG5vdGluZyB3aGV0aGVyIHRoZWlyIGZ1bGwtdGV4dCB3YXMgb2J0YWluZWQgYXJlIGFzIGZvbGxvd3M6CgpgYGB7cn0KaHNwIHw+CiAgICBkcGx5cjo6c3VtbWFyaXplKAogICAgICAgIHRpdGxlX21hdGNoID0gYW55KCFpcy5uYSh0aXRsZV9tYXRjaCkpLAogICAgICAgIGZ0X21hdGNoID0gYW55KCFpcy5uYShmdF9tYXRjaCkpLAogICAgICAgIGZ0ID0gdW5pcXVlKGZ0KSwKICAgICAgICAuYnkgPSAiaWQiCiAgICApIHw+CiAgICBkcGx5cjo6Y291bnQoZnQsIHRpdGxlX21hdGNoLCBmdF9tYXRjaCkgfD4KICAgIGRwbHlyOjptdXRhdGUocGN0ID0gcm91bmQobiAvIHN1bShuKSAqIDEwMCwgMikpIHw+CiAgICBkcGx5cjo6cmVuYW1lKGZ0X29idGFpbmVkID0gImZ0IikKYGBgCk1vc3Qgb2YgdGhlIHB1YmxpY2F0aW9ucyBkbyBub3QgY29udGFpbiBvbmUgb2YgdGhlc2UgdGVybXMsIGluY2x1ZGluZyB+MjAlIG9mIHRob3NlIHRoYXQgd2VyZSBkb3dubG9hZGVkIGFzIGZ1bGwgdGV4dC4gQW55IG5vbi1tYXRjaGVzIHdpbGwganVzdCBiZSBkcm9wcGVkIGZvciB0aGUgYW5hbHlzaXMgb2YgbmFtZXMuCgpgYGB7cn0KaHNwX21hdGNoIDwtIGhzcCB8PgogICAgdGlkeXI6OnBpdm90X2xvbmdlcigKICAgICAgICB0aXRsZV9tYXRjaDpmdF9tYXRjaCwKICAgICAgICBuYW1lc190byA9IGMoInNvdXJjZSIsICIudmFsdWUiKSwKICAgICAgICBuYW1lc19zZXAgPSAiXyIsCiAgICAgICAgdmFsdWVzX2Ryb3BfbmEgPSBUUlVFCiAgICApIHw+CiAgICBkcGx5cjo6bXV0YXRlKG1hdGNoID0gc3RyaW5ncjo6c3RyX3RvX2xvd2VyKC5kYXRhJG1hdGNoKSkgfD4KICAgIGRwbHlyOjpzZWxlY3QoLSJ0aXRsZSIpIHw+CiAgICAjIG9ubHkgY291bnQgb25jZSBwZXIgcHVibGljYXRpb24gYW5kIHRpdGxlL2Z1bGwtdGV4dAogICAgRE8udXRpbHM6OmNvbGxhcHNlX2NvbCgibWF0Y2giKQpgYGAKClRoZSB0ZXJtcyBvZiBpbnRlcmVzdCB0aGF0IGFuZCB3aGV0aGVyIHRoZXkgd2VyZSBmb3VuZCBpcyBhcyBmb2xsb3dzOgpgYGB7cn0KdGVybXNfbWF0Y2ggPC0gdGliYmxlOjp0aWJibGUoCiAgICB0ZXJtcyA9IHRlcm1zLAogICAgbWF0Y2ggPSBzdHJpbmdyOjpzdHJfdG9fbG93ZXIodGVybXMpCikKCnRlcm1zX21hdGNoIHw+CiAgICBkcGx5cjo6bGVmdF9qb2luKGRwbHlyOjpjb3VudChoc3BfbWF0Y2gsIG1hdGNoKSwgYnkgPSAibWF0Y2giKSB8PgogICAgZHBseXI6Om11dGF0ZShuID0gdGlkeXI6OnJlcGxhY2VfbmEobiwgMCkpIHw+CiAgICBkcGx5cjo6c2VsZWN0KC0ibWF0Y2giKSB8PgogICAgZHBseXI6OmFycmFuZ2UoZHBseXI6OmRlc2MoLmRhdGEkbikpCmBgYApPbmx5IHR3byB0ZXJtcyB3ZXJlbid0IGZvdW5kLiBPbmUgaXMgbGlrZWx5IGhpc3RvcmljYWwgYW5kIG1heSBub3QgYmUgYXZhaWxhYmxlIGluIGEgY29tcHV0YWJsZSBmb3JtIGFuZCB0aGUgb3RoZXIgbG9va3MgbGlrZSBpdCBtYXkgYmUgYSBtaXNzcGVsbGluZy4KClRoZSBjdXJyZW50IG5hbWUgaW4gRE8gaXMgbm90IGluIHRoZSB0b3AgNSBtb3N0IGNvbW1vbiBuYW1lcyBpbiB0aGUgbGl0ZXJhdHVyZS4KCk9yZ2FuaXplZCBieSBwdWJsaWNhdGlvbiBkYXRlIGFuZCBiaW5uZWQgaW50byB5ZWFyIGludGVydmFscywgdGhlIHJlc3VsdHMgYXJlIGFzIGZvbGxvd3M6CmBgYHtyfQpoc3BfZGYgPC0gaHNwX21hdGNoIHw+CiAgICBkcGx5cjo6bGVmdF9qb2luKHRlcm1zX21hdGNoLCBieSA9ICJtYXRjaCIpIHw+CiAgICBkcGx5cjo6c2VsZWN0KC0ibWF0Y2giKSB8PgogICAgZHBseXI6OnJlbmFtZSh0ZXJtID0gInRlcm1zIikgfD4KICAgIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKHRlcm0pKQoKaHNwX2NvbG9ycyA8LSBodWVzOjppd2FudGh1ZShkcGx5cjo6bl9kaXN0aW5jdChoc3BfZGYkdGVybSkpCiAgICAKZ2dwbG90Mjo6Z2dwbG90KGhzcF9kZikgKwogICAgZ2dwbG90Mjo6Z2VvbV9mcmVxcG9seSgKICAgICAgICBnZ3Bsb3QyOjphZXMoeCA9IHB1YkRhdGUsIGNvbG9yID0gdGVybSksCiAgICAgICAgYmlud2lkdGggPSAzNjUKICAgICkgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGhzcF9jb2xvcnMpICsKICAgIGdncGxvdDI6OmZhY2V0X3dyYXAofiBzb3VyY2UsIG5jb2wgPSAxLCBzY2FsZXMgPSAiZnJlZV95IikKYGBgCgpIbW0uLi4gdGhlIG9sZGVzdCB1c2VzIGFyZSBxdWl0ZSBhIGxvbmcgdGltZSBhZ28gYW5kIG1ha2UgdGhlIGdyYXBoIGEgYml0IGhhcmQgdG8gcmVhZC4gU3Vic2V0dGluZyB0aGUgZ3JhcGggdG8gYWZ0ZXIgdGhlIHllYXIgMjAwMDoKYGBge3Igd2FybmluZz1GQUxTRX0KZyA8LSBnZ3Bsb3QyOjpnZ3Bsb3QoaHNwX2RmKSArCiAgICBnZ3Bsb3QyOjpnZW9tX2ZyZXFwb2x5KAogICAgICAgIGdncGxvdDI6OmFlcyh4ID0gcHViRGF0ZSwgY29sb3IgPSB0ZXJtLCBncm91cCA9IHRlcm0pLAogICAgICAgIGJpbndpZHRoID0gMzY1LAogICAgICAgIGxpbmV3aWR0aCA9IDEKICAgICkgKwogICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGhzcF9jb2xvcnMpICsKICAgIGdncGxvdDI6OnNjYWxlX3hfZGF0ZSgKICAgICAgICBkYXRlX2JyZWFrcyA9ICI1IHllYXJzIiwKICAgICAgICBkYXRlX2xhYmVscyA9ICIlWSIKICAgICkgKwogICAgZ2dwbG90Mjo6ZmFjZXRfd3JhcCh+IHNvdXJjZSwgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgICBnZ3Bsb3QyOjpjb29yZF9jYXJ0ZXNpYW4oCiAgICAgICAgeGxpbSA9IGMoYXMuRGF0ZSgiMjAwMC0wMS0wMSIpLCBhcy5EYXRlKCIyMDI1LTAxLTAxIikpCiAgICApICsKICAgIGdncGxvdDI6OnRoZW1lX21pbmltYWwoKQoKcGxvdGx5OjpnZ3Bsb3RseShnKQpgYGAKCkJhc2VkIG9uIHRoaXMsIGl0IHNlZW1zIHRoYXQgZm9yIG1vc3Qgb2YgdGhlIERPJ3MgaGlzdG9yeSwgdGhpcyBkaXNlYXNlIGhhcyBwcmltYXJpbHkgYmVlbiBjYWxsZWQgIkhlbm9jaC1TY2jDtm5sZWluIHB1cnB1cmEiIHdoaWNoIHdhcyB0aGUgb3JpZ2luYWwgbmFtZSBvZiB0aGUgcHVibGlzaGVkIGRpc2Vhc2UuIEl0J3MgbGlrZWx5IHRoaXMgd2FzIG5vdCB1c2VkIGJ5IHRoZSBETyBiZWNhdXNlIG9mIHBvdGVudGlhbCBlbmNvZGluZyBpc3N1ZXMgd2l0aCB0aGUgIsO2IiBhbmQsIGluIHRoZSBsaXRlcmF0dXJlLCBpdCBhcHBlYXJzIHRoYXQgdXNpbmcgYSBub3JtYWwgIm8iIHdhcyBuZXh0IG1vc3QgY29tbW1vbiwgYnV0IHRoZSBjdXJyZW50IGxhYmVsIGluIERPIGhhcyBiYXJlbHkgYmVlbiB1c2VkIGFuZCBzaG91bGQgbGlrZWx5IE5PVCBiZSB1c2VkIGFzIHRoZSBsYWJlbC4KCkZ1cnRoZXJtb3JlLCBzaW5jZSB+MjAyMCwgdGhlIG5hbWUgIklnQSB2YXNjdWxpdGlzIiBoYXMgYmVlbiB1c2VkIG1vcmUgZnJlcXVlbnRseSBpbiBwdWJsaWNhdGlvbnMuIEFuIE5JRERLIHNvdXJjZSBhbHNvIHN1Z2dlc3RzIHRoYXQgdGhpcyBkaXNlYXNlIGhhcyBiZWVuIHJlbmFtZWQgdG8gSWdBIHZhc2N1bGl0aXM6IGh0dHBzOi8vd3d3Lm5pZGRrLm5paC5nb3YvaGVhbHRoLWluZm9ybWF0aW9uL2tpZG5leS1kaXNlYXNlL2lnYS12YXNjdWxpdGlzLiBBbHRob3VnaCB0aGUgZnVsbCBuYW1lICJpbW11bm9nbG9idWxpbiBBIHZhc2N1bGl0aXMiIHNlZW1zIGxpa2UgaXQgd291bGQgYmUgbW9yZSBhcHByb3ByaWF0ZSwgaXQgaXMgdXNlZCBmYXIgbGVzcyBmcmVxdWVudGx5LiBUb2dldGhlciwgdGhlc2Ugc3VnZ2VzdCB0aGUgcHJpbWFyeSBuYW1lIHNob3VsZCBiZSB1cGRhdGVkIHRvICJJZ0EgdmFzY3VsaXRpcyIuCgpUaGUgaW5pdGlhbGlzbXMgZm9yIHRoZXNlIG5hbWVzIGFyZSB1c2VkIGZhciBsZXNzIGZyZXF1ZW50bHksIHdpdGggSFNQIGJlaW5nIGdyZWF0ZXIgdGhhbiBJZ0FWLiBJdCdzIHBvc3NpYmxlIHRoYXQgSFNQIHJlcHJlc2VudHMgc29tZXRoaW5nIGVsc2UgZ2l2ZW4gaXQncyBtb3JlIGNvbW1vbiAoZS5nLiAiaGVhdCBzaG9jayBwcm90ZWlucyIsICJoZXJlZGl0YXJ5IHNwYXN0aWMgcGFyYXBsZWdpYSIpLgoKCiMjIyBDb25maXJtaW5nIHJlbGV2YW5jZSBvZiBIU1AKCkp1c3QgdG8gY29uZmlybSB0aGF0IEhTUCBpcyB1c2VkIGluIGNvbmp1bmN0aW9uIHdpdGggdGhpcyBkaXNlYXNlIG5hbWU6CmBgYHtyfQpkcGx5cjo6Y291bnQoaHNwX2RmLCB0ZXJtLCBzb3VyY2UpIHw+CiAgICBkcGx5cjo6ZmlsdGVyKHRlcm0gPT0gIkhTUCIpCmBgYAoKSXQgc2VlbXMgdG8gYXBwZWFyIGluIHRpdGxlcy4gTG9va2luZyBhdCB3aGljaCB0aG9zZSBhcmU6CmBgYHtyfQpkcGx5cjo6ZmlsdGVyKGhzcCwgdGl0bGVfbWF0Y2ggPT0gIkhTUCIpJHRpdGxlCmBgYAoKV2VsbCwgaXQgaXMgdXNlZCB0byBtZWFuIHRoaXMgZGlzZWFzZSBidXQgY2xlYXJseSBhbHNvIHVzZWQgZm9yIG90aGVyIHRoaW5ncy4gSXQgc2VlbXMgbGlrZSBpdCBzaG91bGQgYmUgYWRkZWQgYXMgYSBzeW5vbnltLgoKCiMgQ09OQ0xVU0lPTgoKTmV3IHByaW1hcnkgbGFiZWw6ICJJZ0EgdmFzY3VsaXRpcyIKCkVuc3VyZSBhbGwgdGhlc2UgdGVybXMgYXJlIGluY2x1ZGVkIGFzIHN5bm9ueW1zIGluIHRoZSBETzoKCgpgYGB7ciByZXN1bHRzPSdhc2lzJywgZWNobyA9IEZBTFNFfQpjYXQoCiAgICBzcHJpbnRmKCcqICVzICAgJyx0ZXJtc1t0ZXJtcyAlaW4lIGhzcF9kZiR0ZXJtXSksCiAgICBzZXAgPSAnXG4nCikKYGBgCgpFdmVuIHRob3NlIHRoZXNlIHRlcm1zIHdlcmUgbm90IGZvdW5kIGluIHRoaXMgYW5hbHlzaXMsIHRoZXJlJ3MgYSBwb3NzaWJpbGl0eSB0aGF0IHRoZXkgaGF2ZSBiZWVuIHVzZWQsIGhpc3RvcmljYWxseSBvciBwZXJoYXBzIGFjY2lkZW50YWxseSwgc28gdGhleSB3aWxsIGJlIGxlZnQgaW4gdGhlIG9udG9sb2d5LCBpZiB0aGV5IGFyZSB0aGVyZSBhbHJlYWR5IGJ1dCBub3QgYWRkZWQuCmBgYHtyIHJlc3VsdHM9ImFzaXMiLCBlY2hvID0gRkFMU0V9CmNhdCgKICAgIHNwcmludGYoJyogJXMgICAnLHRlcm1zWyF0ZXJtcyAlaW4lIGhzcF9kZiR0ZXJtXSksCiAgICBzZXAgPSAnXG4nCikKYGBgCgoKT24gcmV2aWV3LCBpdCBhcHBlYXJzIHRoYXQgb25seSAiQXV0b2ltbXVuZSBwdXJwdXJhIiBpcyBhbHJlYWR5IGluIHRoZSBvbnRvbG9neS4gVGhhdCB3aWxsIGJlIGtlcHQgYW5kIHRoZSBvdGhlciBwb3RlbnRpYWwgc3lub255bSB3aWxsIG5vdCBiZSBhZGRlZC4K